﻿using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using UnitShared;
using tactics.Interfaces;
using tactics.GUI;
using tactics.GUI.Menu;

using GameStateManagement;
using TomShane.Neoforce.Controls;

namespace tactics
{
    public class TacticsEngine : Microsoft.Xna.Framework.Game
    {
        
        #region Class Members

        static TacticsEngine instance;
        TacticsGame game;

        ScreenManager screenManager;
        SpriteBatch spriteBatch;
        ConfigManager config;
        //SpriteHelper spriteHelper;
        
        GraphicsDeviceManager graphicsManager;
        GraphicsDevice graphics;

        AudioEngine audioEngine;
        SoundBank soundBank;
        WaveBank waveBank;

        SpriteFont basicFont;

        Texture2D mouseTexture;
        MouseState oldMouseState;
        MouseState mouseState;
        bool mouseEnabled;

        KeyboardState keyState;
        Keys[] keysDown;

        int frameRate = 0;
        int frameCounter = 0;

        TimeSpan elapsedTime = TimeSpan.Zero;

        Manager guiManager;

        AnimationManager animationManager;

        MainMenuScreen mainMenu;
        InGameMenu inGameMenuScreen;
        #endregion

        #region Properties

        public static TacticsEngine Instance
        {
            get
            {
                return instance;
            }
        }

        public GraphicsDevice Graphics
        {
            get
            {
                return graphics;
            }
        }

        public GraphicsDeviceManager GraphicsManager
        {
            get
            {
                return graphicsManager;
            }
        }

        public Manager GuiManager
        {
            get
            {
                return guiManager;
            }
        }

        public SpriteBatch SpriteBatch
        {
            get
            {
                return spriteBatch;
            }
        }

        public AnimationManager AnimationManager
        {
            get
            {
                return animationManager;
            }
        }

        public SoundBank SoundBank
        {
            get
            {
                return soundBank;
            }
        }

        public bool MouseEnabled
        {
            get
            {
                return mouseEnabled;
            }
            set
            {
                mouseEnabled = value;
            }
        }

        public Texture2D MouseTexture
        {
            get
            {
                return mouseTexture;
            }
        }
        public ConfigManager Config
        {
            get
            {
                return config;
            }
        }

        public TacticsGame Game
        {
            get
            {
                return game;
            }
        }

        #endregion
        
        #region Construction

        public TacticsEngine()
        {
            instance = this;

            config = new ConfigManager("UserConfig.xml");

            graphicsManager = new GraphicsDeviceManager(this);
            SetScreen(true);

            Content.RootDirectory = "Content";
            
            guiManager = new Manager(this,graphicsManager, "Default", true);
            guiManager.SkinDirectory = @"Content\Skins\";

            screenManager = new ScreenManager(this);

            animationManager = new AnimationManager();

            Components.Add(screenManager);

            this.IsMouseVisible = true;


            // uncomment these for maximum frame rate
            graphicsManager.SynchronizeWithVerticalRetrace = false;
            this.IsFixedTimeStep = false;
        }

        #endregion

        #region Initialization

        protected override void Initialize()
        {
            base.Initialize();

            graphics = graphicsManager.GraphicsDevice;

            mainMenu = new MainMenuScreen(guiManager);
            
            screenManager.AddScreen(mainMenu, null);
            //screenManager.AddScreen(new OptionsScreen(guiManager), null);

            audioEngine = new AudioEngine("Content\\Audio\\Win\\tactics.xgs");
            soundBank = new SoundBank(audioEngine, "Content\\Audio\\Win\\Sound Bank.xsb");
            waveBank = new WaveBank(audioEngine, "Content\\Audio\\Win\\Wave Bank.xwb");
            
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            mouseEnabled = true;

            Window.Title = "Tactics";
        }

        public void StartGame()
        {
            this.IsMouseVisible = false;
            game = new TacticsGame();
        }

        public void QuitGame()
        {
            this.IsMouseVisible = true;
            game.RemoveGuiElements();
            game = null;
            guiManager = new Manager(this, graphicsManager, "Default", true);
            mainMenu = new MainMenuScreen(guiManager);
            screenManager.AddScreen(mainMenu, null);
        }

        #endregion

        #region Content Management

        protected override void LoadContent()
        {
            mouseTexture = Content.Load<Texture2D>("swordcursor");
            basicFont = Content.Load<SpriteFont>("Kootenay");
        }

        protected override void UnloadContent()
        {

        }

        #endregion

        #region Key Detection

        private void KeysUpdate()
        {
            // check for new key presses
            keyState = Keyboard.GetState();
            Keys[] newKeys = (Keys[])keyState.GetPressedKeys();

            if (keysDown != null)
            {
                // look for new key presses
                foreach (Keys k1 in newKeys)
                {
                    bool oldKeyDown = false;

                    foreach (Keys k2 in keysDown)
                    {
                        if (k1 == k2)
                        {
                            oldKeyDown = true;
                            break;
                        }
                    }

                    if (!oldKeyDown)
                    {
                        OnKeyPressed(k1);
                    }
                }

                // look for key raises
                foreach (Keys k1 in keysDown)
                {
                    bool keyUp = true;

                    foreach (Keys k2 in newKeys)
                    {
                        if (k1 == k2)
                        {
                            keyUp = false;
                            break;
                        }
                    }

                    if (keyUp)
                    {
                        OnKeyUp(k1);
                    }
                }
            }

            keysDown = newKeys;
        }

        protected void OnKeyUp(Keys k)
        {
            // add functions that are necessary on KeyUp-only here
            if (game != null)
            {
                game.OnKeyUp(k);
            }
        }

        protected void OnKeyPressed(Keys k)
        {
            // Allows the game to exit
            if (k == config.Keys["Quit"])

                if (game != null)
                {
                    if (inGameMenuScreen == null)
                    {
                        inGameMenuScreen = new InGameMenu(guiManager);
                    }
                    
                    if (!screenManager.GetScreens().Contains(inGameMenuScreen))
                    {
                        screenManager.AddScreen(inGameMenuScreen, null);
                    }
                }

            if (k == config.Keys["ToggleFullscreen"])
            {
                Config.ToggleFullscreen();

                SetScreen(false);
            }

            if (game != null)
            {
                game.OnKeyPressed(k);
            }
        }

        #endregion

        #region Update

        protected override void Update(GameTime gameTime)
        {
            UpdateFPS(gameTime);
            audioEngine.Update();
            animationManager.Update(gameTime);
            KeysUpdate();
            mouseState = Mouse.GetState();

            // try / catch
            base.Update(gameTime);

            if (game != null)
            {
                game.Update(gameTime, mouseState, oldMouseState);
            }

            oldMouseState = mouseState;

            var gps = GamePad.GetState(PlayerIndex.One);
            if (gps.IsConnected)
            {
                if (gps.Buttons.X == ButtonState.Pressed)
                    this.Exit();

                float rotate = gps.ThumbSticks.Right.X;
                float panX = -gps.ThumbSticks.Left.X;
                float panY = gps.ThumbSticks.Left.Y;

                if (game != null)
                {
                    this.game.Camera.RotateCamera(rotate * 2f);
                    this.game.Camera.ScrollCamera(panY * 0.5f, panX * 0.5f);
                }
            }

            //Execute the next command in the command queue
            CommandQueue.Instance.InvokeNext();
        }

        #endregion

        #region Rendering

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            if (game != null)
                game.Draw(gameTime);

            // gui drawn in game loop
            base.Draw(gameTime);

            if (game != null)
                game.DrawCursor();

            DrawFPS(gameTime);
        }

        private void SetScreen(bool init)
        {
            Utility.Pair<int, int> screen = config.Display[Config.DisplayMode];

            graphicsManager.PreferredBackBufferWidth = screen.First;
            graphicsManager.PreferredBackBufferHeight = screen.Second;

            // when these disagree, it is because we are trying to toggle between modes
            if (graphicsManager.IsFullScreen != config.Fullscreen)
                graphicsManager.ToggleFullScreen();

            // the following isn't needed during initialization,
            // only when the game is loaded
            if (!init)
            {
                game.SetScreen();
            }
        }

        #endregion

        #region FPS handling

        private void UpdateFPS(GameTime gameTime)
        {
            elapsedTime += gameTime.ElapsedGameTime;

            if (elapsedTime > TimeSpan.FromSeconds(1))
            {
                elapsedTime -= TimeSpan.FromSeconds(1);
                frameRate = frameCounter;
                frameCounter = 0;
            }
        }

        private void DrawFPS(GameTime gameTime)
        {
            // Draw the text
            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);

            frameCounter++;
            spriteBatch.DrawString(basicFont, "FPS: " + frameRate, new Vector2(graphicsManager.PreferredBackBufferWidth / 2 - 40, graphicsManager.PreferredBackBufferHeight - 30), Color.Yellow);

            spriteBatch.End();
        }

        #endregion
       
    }
}